﻿// OPCDA.PascalMing.KingView.InitItems.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>


#include <objbase.h>
#include <vector>
#include "tinyxml/tinystr.h"
#include "tinyxml/tinyxml.h"

#include "Include/opcda_i.c"

#pragma warning(disable: 26482)
#pragma warning(disable: 26493)
#pragma warning(disable: 26496)

//#include "opcenum_clsid.c"

#include "Include/Opcda.h"

#include "Include/opccomn.h"
#include <process.h>

using namespace std;

#define OPC_S_UNSUPPORTEDRATE ((HRESULT)0x0004000DL)

WINOLEAPI CoCreateInstanceEx(IN REFCLSID Clsid, IN IUnknown* punkOuter, IN DWORD dwClsCtx, IN COSERVERINFO* pServerInfo, IN DWORD dwCount, IN OUT MULTI_QI* pResults);



void XmlAddNode(TiXmlElement* node, const char* child, const char* value)
{
	TiXmlElement* nodeChild = new TiXmlElement(child);
	node->LinkEndChild(nodeChild);

	nodeChild->LinkEndChild(new TiXmlText(value));
}
void XmlAddNode(TiXmlElement* node, const char* child, const char* attr, const char* value)
{
	TiXmlElement* nodeChild = new TiXmlElement(child);
	node->LinkEndChild(nodeChild);
	nodeChild->SetAttribute(attr, value);
}


//支持的数据类型由UtilityClasses\COpcXmlAnyType.cpp定义，可以自行增加
const char* XmlGetVtString(VARTYPE vt)
{
	char buf[100] = "unknown";
	const char* pStr = buf;
	switch (vt)
	{
	case VT_I2:      pStr = "xsd:short"; break;
	case VT_I4:      pStr = "xsd:int"; break;
	case VT_R4:      pStr = "xsd:float"; break;
	case VT_R8:      pStr = "xsd:double"; break;
	case VT_BSTR:    pStr = "xsd:string"; break;
	case VT_I1:      pStr = "xsd:byte"; break;
	case VT_UI1:     pStr = "xsd:unsignedByte"; break;
	case VT_UI2:     pStr = "xsd:unsignedShort"; break;
	case VT_UI4:     pStr = "xsd:unsignedInt"; break;
	case VT_I8:      pStr = "xsd:long"; break;
	case VT_UI8:     pStr = "xsd:unsignedLong"; break;
	case VT_DECIMAL: pStr = "xsd:decimal"; break;
	case VT_INT:     pStr = "xsd:int"; break;
	case VT_UINT:    pStr = "xsd:unsignedInt"; break;
	case VT_DATE:    pStr = "xsd:dateTime"; break;
	case VT_VARIANT: pStr = "xsd:anyType"; break;
	case VT_BOOL:    pStr = "xsd:boolean"; break;
		//case VT_BLOB: pStr = ""; break;
		//case VT_ARRAY: pStr = ""; break;
		//case VT_CY:    pStr = ""; break;
		//case VT_FILETIME: pStr = ""; break;
		//case VT_DISPATCH: pStr = ""; break;
		//case VT_ERROR: pStr = ""; break;
		//case VT_UNKNOWN: pStr = ""; break;
		//case VT_RECORD: pStr = ""; break;
		//case VT_INT_PTR: pStr = ""; break;
		//case VT_UINT_PTR: pStr = ""; break;
		//case VT_HRESULT: pStr = ""; break;
		//case VT_PTR: pStr = ""; break;
		//case VT_SAFEARRAY: pStr = ""; break;
		//case VT_CARRAY: pStr = ""; break;
		//case VT_USERDEFINED: pStr = ""; break;
		//case VT_VOID: pStr = ""; break;
		//case VT_LPSTR: pStr = ""; break;
		//case VT_LPWSTR: pStr = ""; break;
		//case VT_STREAM: pStr = ""; break;
		//case VT_STORAGE: pStr = ""; break;
		//case VT_STREAMED_OBJECT: pStr = ""; break;
		//case VT_STORED_OBJECT: pStr = ""; break;
		//case VT_VERSIONED_STREAM: pStr = ""; break;
		//case VT_BLOB_OBJECT: pStr = ""; break;
		//case VT_CF: pStr = ""; break;
		//case VT_CLSID: pStr = ""; break;
		//case VT_VECTOR: pStr = ""; break;
		//case VT_BSTR_BLOB: pStr = ""; break;
		//case VT_BYREF: pStr = ""; break;
	default:
		sprintf_s(buf, 99, "xsd:VT=%d", vt);
	}

	return pStr;
}

void XmlAddNode(TiXmlElement* node, const char* elName, VARTYPE vt)
{
	TiXmlElement* nodeEl = new TiXmlElement("BrowseElement");
	node->LinkEndChild(nodeEl);
	nodeEl->SetAttribute("ElementName", elName);

	TiXmlElement* nodeItem = new TiXmlElement("Item");
	nodeEl->LinkEndChild(nodeItem);

	TiXmlElement* nodeValue = new TiXmlElement("Value");
	nodeItem->LinkEndChild(nodeValue);

	nodeValue->SetAttribute("xsi:type", XmlGetVtString(vt));
	nodeValue->LinkEndChild(new TiXmlText("0"));
}

int dwTagItemCount = 0;

int main()
{
    cout << "OPCDA.PascalMing.KingView.InitItems Start...!\n";

	HRESULT hr = NULL;
	IOPCServer* pIOPCServer = { NULL };
	//GUID位置 计算机\HKEY_CLASSES_ROOT\KingView.View\CLSID
	GUID clsID = { 0x21C3E5E0,0xB7E4,0x11d4,{0x8B,0xE0,0x00,0x50,0xBA,0xCF,0xBB,0x99} };

	//GUID不清楚是，可以用CLSIDFromString,由OPC Name获取GUID
	//wchar_t szOPCServerName[] = { L"KingView.View.1" };  //OPC Name
	//hr = CLSIDFromString(szOPCServerName, &clsID);

	MULTI_QI m_arrMultiQI[1];
	for (int i = 0; i < sizeof(m_arrMultiQI) / sizeof(MULTI_QI); i++)
	{
		m_arrMultiQI[i].pItf = NULL;
		m_arrMultiQI[i].hr = 0;
	}


	m_arrMultiQI[0].pIID = &IID_IOPCServer;

	hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

	cout << "CoInitializeEx hr=" << hr << endl;

	hr = CoCreateInstanceEx(
		clsID,
		NULL,
		CLSCTX_SERVER,//IsRemote?CLSCTX_REMOTE_SERVER:CLSCTX_LOCAL_SERVER,					
		NULL,
		sizeof(m_arrMultiQI) / sizeof(MULTI_QI),
		m_arrMultiQI);

	if (hr != S_OK)
	{
		if (hr == 0x800702e4)
		{
			cout << "CoCreateInstanceEx fail!,hr = HRESULT_FROM_WIN32(ERROR_ELEVATION_REQUIRED) : 请求的操作需要提升权限。\n";
		}
		else if (hr == 0x800401f0)
		{
			cout << "CoCreateInstanceEx fail!,hr = 0x800401f0 : 尚未调用 CoInitialize。 \n";
		}
		else
		{
			cout << "CoCreateInstanceEx error hr=" << hr << endl;
		}

		return hr;
	}
	cout << "CoCreateInstanceEx success!\n";

	pIOPCServer = (IOPCServer*)(m_arrMultiQI[0].pItf);


	//遍历OPC Server所有Tag
	LPENUMSTRING pPENUMSTRING;

	IOPCBrowseServerAddressSpace* pBrowser = NULL;
	pIOPCServer->QueryInterface(IID_IOPCBrowseServerAddressSpace, (void**)&pBrowser);
	cout << "QueryInterface IID_IOPCBrowseServerAddressSpace\n";

	IOPCItemProperties* pIOPCItemProperties = NULL;
	pIOPCServer->QueryInterface(IID_IOPCItemProperties, (void**)&pIOPCItemProperties);
	cout << "QueryInterface IID_IOPCItemProperties\n";

	hr = pBrowser->BrowseOPCItemIDs(OPC_FLAT, L"", VT_EMPTY, 0, &pPENUMSTRING);
	cout << "BrowseOPCItemIDs start,hr=" << hr << endl;;
	cout << "please wait a moment...\n";

	TiXmlDocument* doc = new TiXmlDocument();
	TiXmlDeclaration* pDeclaration = new TiXmlDeclaration("1.0", "UTF-8", "");
	doc->LinkEndChild(pDeclaration);

	TiXmlElement* RootServer = new TiXmlElement("Server");
	doc->LinkEndChild(RootServer);

	RootServer->SetAttribute("xmlns", "http://schemas.opcfoundation.org/OPC/");
	RootServer->SetAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
	RootServer->SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");

	TiXmlElement* NodeSelfRegInfo = new TiXmlElement("SelfRegInfo");
	RootServer->LinkEndChild(NodeSelfRegInfo);
	XmlAddNode(NodeSelfRegInfo, "VendorName", "PascalMing4KingView by Group");
	XmlAddNode(NodeSelfRegInfo, "ApplicationName", "OpcDA");
	XmlAddNode(NodeSelfRegInfo, "ApplicationDescription", "OPC Data Access 3.0 PascalMing for KingView Server");
	XmlAddNode(NodeSelfRegInfo, "AppID", "{5DBE5870-915C-4667-BAA1-C04B11187307}");
	XmlAddNode(NodeSelfRegInfo, "ClassName", "OpcDA");
	XmlAddNode(NodeSelfRegInfo, "ClassDescription", "OPC Data Access 3.0 PascalMing for KingView Server");
	XmlAddNode(NodeSelfRegInfo, "ClassVersion", "1");
	XmlAddNode(NodeSelfRegInfo, "CLSID", "{e19264c8-5e01-4ad1-9be0-e74b75dcbb21}");


	ULONG count = 0;
	LPOLESTR pItemName = NULL, pItemID = NULL;
	while (pPENUMSTRING->Next(1, &pItemName, &count) == S_OK)
	{
		pBrowser->GetItemID(pItemName, &pItemID);

		DWORD dwCount = 1;
		DWORD* dwPropertyIDs = NULL;
		LPWSTR* ppDescriptions = NULL;
		VARTYPE* ppvtDataTypes = NULL;
		VARIANT* ppvData = NULL;
		HRESULT* ppErrors = NULL;
		HRESULT hr2 = pIOPCItemProperties->QueryAvailableProperties(pItemID, &dwCount, &dwPropertyIDs, &ppDescriptions, &ppvtDataTypes);
		dwCount = 1;//仅取第一个数据，即数据类型数据。第二个数据为当前值，可以由订阅获取
		HRESULT hr3 = pIOPCItemProperties->GetItemProperties(pItemID, dwCount, dwPropertyIDs, &ppvData, &ppErrors);

		VARTYPE vtItem = ppvData->iVal;

		char mItemIdName[512] = { 0 };
		WideCharToMultiByte(CP_UTF8, NULL, pItemID, -1, mItemIdName, 512, NULL, FALSE);
		XmlAddNode(RootServer, mItemIdName, vtItem);

		CoTaskMemFree(dwPropertyIDs);
		CoTaskMemFree(ppDescriptions);
		CoTaskMemFree(ppvtDataTypes);
		CoTaskMemFree(ppvData);
		CoTaskMemFree(ppErrors);

		dwTagItemCount++;
	}
	pIOPCItemProperties->Release();
	pBrowser->Release();
	pIOPCServer->Release();


	cout << "BrowseOPCItemIDs end, Items=" << dwTagItemCount << endl;;

	const char* szSaveFile = "OPCDA.PascalMing.KingView.config.xml";
	doc->SaveFile(szSaveFile);

	cout <<"SaveFile:" << szSaveFile << endl;
	cout <<"success!\n\n";

	cout << "Press ENTER key return!";

	std::getchar();

	return 0;
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件
